/*	--- IOC 原理 --- 

【Spring】提供的【容器】又称为【IoC容器】，什么是IoC？

【IoC】全称【Inversion of Control】，直译为【控制反转】。

Q: 那么何谓IoC？

在理解IoC之前，我们先看看通常的[Java组件]是【如何协作】的。

--------------------

我们假定【一个在线书店】，通过【class BookService】获取书籍： */
public class BookService { 

	private HikariConfig config = new HicariConfig();
	private DateSource dataSource = new HikariDataSource(config);

	public Book getBook(long bookId) {
		try (Connection conn = dataSource.getConnection()) {
			//...

			return book;
		}
	}
}


/*
为了从【数据库】查询书籍, 【class BookService】持有一个【DataSource】。

为了【实例化一个HikariDataSource】，又不得不【实例化一个HikariConfig】。

-----------------------------------------

现在，我们继续编写【class UserService】获取用户： */
public class UserService {

	private HikariConfig config = new HikariConfig();
	private DataSource dataSource = new HikdariDataSource(config);

	public User getUser(long userId) {
		try (Connection conn = dataSource.getConnection()) {
			//...

			return user;
		}
	}
}

/*
因为【UserService】也需要【访问数据库】

因此，我们不得不也【实例化一个HikariDataSource】。

-------------------------------

在【处理用户购买】的【class CartServlet】中, 我们又需要【实例化 UserService 和 BookService】： */
public class CartServlet extends HttpServlet {
	private BookService bookService = new BookService();
	private UserService userService = new UserService();

	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException,IOException {

		long currentUserId = getFromCookie(req);

		User currentUser = userService.getUser(currentUserId);
		Book book = bookService.getBook(req.getParameter("bookId"));
		cartService.addToCart(currentUser, book);
		//...

	} 
}


/*
类似的，在【购买历史 class HistoryServlet】中

也需要【实例化 UserService 和 BookService】： */
public class HistoryServlet extends HttpServlet {
	private BookService bookService = new BookService();
	private UserService userServcie = new UserService();
}


/*
上述【每个组件】都采用了一种简单的【通过new创建实例 并 持有的方式】。

仔细观察，会发现以下缺点：

1. 【实例化一个组件】其实很难。 
例如，BookService和UserService要创建HikariDataSource，实际上需要读取配置，才能先实例化HikariConfig，再实例化HikariDataSource。

2. 没有必要让【BookService和UserService】分别创建【DataSource实例】，完全可以【共享同一个DataSource】，但谁负责创建DataSource，谁负责获取【其他组件】已经创建的【DataSource】，不好处理。
类似的，CartServlet 和 HistoryServlet 也应当【共享】【BookService实例】和【UserService实例】，但也不好处理。

3. 【很多组件】需要【销毁】以便释放资源。
例如DataSource，但如果【DataSource该组件】被【多个组件】共享，如何确保【它的使用方】都已经【全部被销毁】？

4. 随着【更多的组件】被引入，例如，书籍评论，需要【共享的组件】写起来会更困难，这些【组件的依赖关系】会越来越复杂。

5. 测试【某个组件】，例如BookService，是复杂的，因为必须要在【真实的数据库环境】下执行。


-------------------------------------


从上面的例子可以看出，如果【一个系统】有【大量的组件】，其【生命周期】和【相互之间的依赖关系】如果由【组件自身】来维护，不但大大增加了【系统的复杂度】;

而且会导致【组件之间】极为【紧密的耦合】，继而给【测试】和【维护】带来了【极大的困难】。

-----------------------

因此，核心问题是：

	1. 谁负责【创建组件】？
	2. 谁负责【根据依赖关系】组装组件？
	3. 销毁时，如何【按依赖顺序】正确销毁？


--------------------------------


【解决这一问题的核心方案】就是【IoC】。

【传统的应用程序】中，【控制权】在【程序本身】，【程序的控制流程】完全由【开发者】控制，例如：

【class CartServlet】创建了【Instance BookService】，在【创建BookService】的过程中，又【创建了DataSource组件】。

【这种模式的缺点】是: 
	【一个组件】如果要使用【另一个组件】，必须先知道【如何正确地创建它】。

--------------------------------

在【IoC模式】下，【控制权】发生了【反转】

即【控制权】从【应用程序】转移到了【IoC容器】

【所有组件】不再由【应用程序自己】创建和配置, 而是由【IoC容器】负责

这样，【应用程序】只需要直接使用【已经创建好并且配置好的组件】。

为了能让【组件】在【IoC容器】中被“装配”出来，需要某种【“注入”机制】

例如: 【BookService自己】并不会创建【DataSource】，而是等待【外部】通过【setDataSource()方法】来注入【一个DataSource】： */
public class BookService {
	private DataSource dataSource;

	public void setDataSource(DataSource dataSource) {
		this.dataSource = dataSource;
	}
}


/*
不直接new一个DataSource，而是【注入一个DataSource】

这个小小的改动虽然简单，却带来了【一系列好处】：

	1. 【BookService】不再关心【如何创建DataSource】，因此，不必编写【读取数据库配置之类的代码】；

	2. 【DataSource实例】被注入到【BookService】，同样也可以【注入到UserService】，因此，【共享一个组件】非常简单；

	3. 【测试BookService】更容易，因为【注入的是DataSource】，可以使用【内存数据库】，而不是【真实的MySQL配置】。

-----------------------------


因此，【IoC】又称为【依赖注入】（DI：Dependency Injection）

【IoC】解决了一个【最主要的问题】：
	将【组件的创建+配置】与【组件的使用】相分离，并且，由【IoC容器】负责管理【组件的生命周期】。

因为【IoC容器】要负责实例化【所有的组件】

因此，有必要告诉【容器】: 
	1.如何【创建组件】，
	2.各【组件的依赖关系】。

-------------------------------

【一种最简单的配置】是通过【XML文件】来实现，例如：
--------------------------------------
	<beans>
	    <bean id="dataSource" class="HikariDataSource" />
	    <bean id="bookService" class="BookService">
	        <property name="dataSource" ref="dataSource" />
	    </bean>
	    <bean id="userService" class="UserService">
	        <property name="dataSource" ref="dataSource" />
	    </bean>
	</beans>

-----------------------------------------

上述【XML配置文件】指示【IoC容器】创建【3个JavaBean组件】，并把【id为dataSource的组件】通过【属性ref="dataSource"】（即调用setDataSource()方法）注入到【另外两个组件】中。

在【Spring的IoC容器】中，我们把【所有组件】统称为【JavaBean】，即【配置一个组件】就是【配置一个Bean】。


========================================================

#	依赖注入方式


我们从上面的代码可以看到，【依赖注入】可以通过【set()方法】实现。

但【依赖注入】也可以通过【构造方法】实现。

很多【Java类】都具有【带参数的构造方法】，如果我们把BookService改造为通过构造方法注入，那么实现代码如下： */
public class BookService {
	private DataSource dataSource;

	public BookService(DataSource dataSource) {
		this.dataSource = dataSource;
	}
}

/*
【Spring的IoC容器】同时支持【属性注入】和【构造方法注入】，并允许【混合使用】。


=====================================================================


#	【无侵入容器】


在设计上，【Spring的IoC容器】是【一个高度可扩展的"无侵入容器"】。

所谓【无侵入】，是指【应用程序的组件】无需实现【Spring的特定接口】，或者说，【组件】根本不知道【自己在Spring的容器中运行】。



这种无侵入的设计有以下好处：
  1. 【应用程序组件】既可以在【Spring的IoC容器】中运行，也可以【自己编写代码】自行组装配置；
  2. 【测试】的时候并【不依赖Spring容器】，可【单独进行测试】，大大提高了【开发效率】。


*/








